﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ProgNetComponents.Ini
{
    public class IniFile
    {
        private string FilePath;
        private Dictionary<string, Dictionary<string, string>> IniValues;

        public IniFile(string filePath)
        {
            this.FilePath = filePath;
            IniValues = new Dictionary<string, Dictionary<string, string>>();
        }

        /// <summary>
        /// Reads file to cache
        /// </summary>
        /// <returns></returns>
        public bool ReadIni()
        {
            bool result = false;
            try
            {
                RewriteIni();
                List<string> Categories = GetCategories();
                Dictionary<string, string> keyValues = null;
                foreach (string cat in Categories)
                {
                    if (IniValues.ContainsKey(cat))
                        keyValues = IniValues[cat];
                    else
                    {
                        keyValues = new Dictionary<string, string>();
                        IniValues.Add(cat, keyValues);
                    }
                    List<string> keys = GetKeys(cat);
                    foreach (string key in keys)
                    {
                        string val = GetKeyValue(cat, key);
                        if (keyValues.ContainsKey(key))
                            keyValues[key] = val;
                        else
                            keyValues.Add(key, val);
                    }
                }
                result = true;
            }
            catch
            {

            }
            return result;
        }
        /// <summary>
        /// Saves all cached data to file
        /// </summary>
        /// <returns></returns>
        public bool SaveIni()
        {
            bool result = true;
            try
            {
                Dictionary<string, string> keyValues = null;
                foreach (string cat in IniValues.Keys)
                {
                    keyValues = IniValues[cat];
                    foreach (KeyValuePair<string, string> kvp in keyValues)
                    {
                        WriteValue(cat, kvp.Key, kvp.Value);
                    }
                }
            }
            catch
            {
                result = false;
            }
            return result;
        }
        /// <summary>
        /// Reads  value directly from file
        /// </summary>
        /// <param name="category"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public string ReadIniValFromFile(string category, string key)
        {
            return GetKeyValue(category, key);
        }
        /// <summary>
        /// Writes value directly to file
        /// </summary>
        /// <param name="category"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool WriteValueToFile(string category, string key, string value)
        {
            WriteValueToCache(category, key, value);
            return WriteValue(category, key, value) > 0;
        }
        /// <summary>
        /// Writes value to cached collection. File has to be saved.
        /// </summary>
        /// <param name="category"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void WriteValueToCache(string category, string key, string value)
        {
            Dictionary<string, string> keyValues = null;
            if (IniValues.ContainsKey(category))
                keyValues = IniValues[category];
            else
            {
                keyValues = new Dictionary<string, string>();
                IniValues.Add(category, keyValues);
            }
            if (keyValues.ContainsKey(key))
                keyValues[key] = value;
            else
                keyValues.Add(key, value);
        }
        /// <summary>
        /// Reads value from cache. File has to be read before.
        /// </summary>
        /// <param name="category"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetValueFormCache(string category, string key)
        {
            if (IniValues.ContainsKey(category))
                if (IniValues[category].ContainsKey(key))
                    return IniValues[category][key];
            return "";
        }
        public bool HasValue(string category, string key)
        {
            if (IniValues.ContainsKey(category))
                if (IniValues[category].ContainsKey(key))
                    return true;
            return false;
        }
        public void DeleteKey(string category, string key)
        {
            if (IniValues.ContainsKey(category))
                if (IniValues[category].ContainsKey(key))
                    IniValues[category].Remove(key);
        }
        private List<string> GetCategories()
        {
            string returnString = new string(' ', 65536);
            GetPrivateProfileString(null, null, null, returnString, 65536, FilePath);
            List<string> result = new List<string>(returnString.ToString().AsString().Split('\0'));
            result.RemoveRange(result.Count - 2, 2);
            return result;
        }
        private List<string> GetKeys(string category)
        {
            string returnString = new string(' ', 65536);
            GetPrivateProfileString(category, null, null, returnString, 65536, FilePath);
            List<string> result = new List<string>(returnString.ToString().AsString().Split('\0'));
            result.RemoveRange(result.Count - 2, 2);
            return result;
        }
        private string GetKeyValue(string category, string key)
        {
            string result = new string(' ', 1024);
            GetPrivateProfileString(category, key, "", result, 1024, FilePath);
            string s = result.ToString().AsString();
            string[] parts = s.Split('\0');
            if (parts.Length > 0)
                return parts[0];
            return s;
        }
        private long WriteValue(string category, string key, string value)
        {
            return WritePrivateProfileString(category, key, value, FilePath);
        }
        private void RewriteIni()
        {
            string str = FilePath.ReadFile().Trim();
            str.WriteToFile(FilePath, Encoding.ASCII);
        }
        #region import

        [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW",
             SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = true,
             CallingConvention = CallingConvention.StdCall)]
        private static extern int GetPrivateProfileString(
          string lpAppName,
          string lpKeyName,
          string lpDefault,
          string lpReturnString,
          int nSize,
          string lpFilename);

        [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW",
          SetLastError = true,
          CharSet = CharSet.Unicode, ExactSpelling = true,
          CallingConvention = CallingConvention.StdCall)]
        private static extern int WritePrivateProfileString(
          string lpAppName,
          string lpKeyName,
          string lpString,
          string lpFilename);
        #endregion

    }
}
